/******************************************************************************
 * This file is part of libemb.
 *
 * libemb is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * libemb is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with libemb.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Project: Embedme
 * Author : FergusZeng
 * Email  : cblock@126.com
 * git	  : https://git.oschina.net/cblock/embedme
 * Copyright 2014~2020 @ ShenZhen ,China
*******************************************************************************/
#include "DateTime.h"
#include "Tracer.h"
#include "StrUtil.h"

#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <fcntl.h>
#include <time.h>

#if defined(OS_CYGWIN) || defined(OS_ANDROID)
#define USE_RTC     0
#else
#include <linux/rtc.h>
#define RTC_DEV_PATH    "/dev/rtc0"
#define USE_RTC     1
#endif

namespace libemb{

Time::Time()
{
	m_seconds = 0;
	m_microSeconds = 0;
}

Time::Time(int sec,int us)
{
	m_seconds = sec+us/1000000;
	m_microSeconds = us%1000000;
}

Time::~Time()
{
}

int Time::secondPart()
{
	return m_seconds;
}

int Time::microSecondPart()
{
	return m_microSeconds;
}

long long Time::toMicroSeconds()
{
	long long microSeconds = m_seconds*1000000;
	return microSeconds+m_microSeconds;
}
std::string Time::toString()
{
	return StrUtil::stringFormat("%d.%06d",m_seconds,m_microSeconds);
}


Time Time::currentTimeFromEpoch()
{
	Time currTime;
	struct timespec currentTs;
	clock_gettime(CLOCK_REALTIME,&currentTs);
	currTime.m_seconds = currentTs.tv_sec;
	currTime.m_microSeconds = (int)(currentTs.tv_nsec/1000);
	return currTime;
}

Time Time::currentTimeMonotonic()
{
	Time currTime;
	struct timespec currentTs;
	clock_gettime(CLOCK_MONOTONIC,&currentTs);
	currTime.m_seconds = currentTs.tv_sec;
	currTime.m_microSeconds = (int)(currentTs.tv_nsec/1000);
	return currTime;
}

int Time::usIntervalMonotonic(Time& monoStartTime)
{
	Time diffTime = Time::currentTimeMonotonic()-monoStartTime;
	return  (int)diffTime.toMicroSeconds();
}


Time Time::operator +(const Time &time)
{
	Time result;
	result.m_seconds = m_seconds + time.m_seconds;
	result.m_microSeconds = m_microSeconds + time.m_microSeconds;
	while (result.m_microSeconds >= 1000000) 
	{
		result.m_seconds++;
	    result.m_microSeconds -= 1000000;
	}
	return result;
}

Time Time::operator -(const Time &time)
{
	Time result;
	result.m_microSeconds = m_microSeconds - time.m_microSeconds;
	if (result.m_microSeconds<0)
	{
		result.m_microSeconds += 1000000;
		result.m_seconds = m_seconds - time.m_seconds -1;
	}	
	else
	{
		result.m_seconds = m_seconds - time.m_seconds;
	}
	return result;
}

DateTime::DateTime()
{
    m_year = 2000;
    m_month = 1;
    m_date = 1;
    m_hour = 0;
    m_minute = 0;
    m_second = 0;
}
DateTime::DateTime(unsigned int year,unsigned int month,unsigned int date,unsigned int hour,unsigned int minute,unsigned int second)
{
    m_year = 2000;
    m_month = 1;
    m_date = 1;
    m_hour = 0;
    m_minute = 0;
    m_second = 0;
    setYear(year);
    setMonth(month);
    setDate(date);
    setHour(hour);
    setMinute(minute);
    setSecond(second);
}

DateTime::DateTime(const DateTime& copy)
{
    m_year = copy.m_year; 
    m_month = copy.m_month;
    m_date = copy.m_date;
    m_hour = copy.m_hour;
    m_minute = copy.m_minute;
    m_second = copy.m_second;
}
DateTime::~DateTime()
{
}
unsigned int DateTime::year()
{
    return m_year;

}
unsigned int DateTime::month()
{
    return m_month;
}
unsigned int DateTime::date()
{
    return m_date;
}
unsigned int DateTime::hour()
{
    return m_hour;
}
unsigned int DateTime::minute()
{
    return m_minute;
}
unsigned int DateTime::second()
{
    return m_second;
}
DateTime& DateTime::setYear(unsigned int year)
{
    if (year>=1970 && year<2100) 
    {
        m_year = year;
    }
    return *this;
}
DateTime& DateTime::setMonth(unsigned int month)
{
    if (month>0 && month<=12) 
    {
        m_month = month;
    }
    return *this;
}
DateTime& DateTime::setDate(unsigned int date)
{
    if (date>0 && date<=31) 
    {
        m_date = date;
    }
    return *this;
}
DateTime& DateTime::setHour(unsigned int hour)
{
    if (hour>=0 && hour<24) 
    {
        m_hour = hour;
    }
    return *this;
}
DateTime& DateTime::setMinute(unsigned int minute)
{
    if (minute>=0 && minute<60) 
    {
        m_minute = minute;
    }
    return *this;
}
DateTime& DateTime::setSecond(unsigned int second)
{
    if (second>=0 && second<60) 
    {
        m_second = second;
    }
    return *this;
}

std::string DateTime::toString()
{
    return StrUtil::stringFormat("%d-%02d-%02d %02d:%02d:%02d", m_year, m_month, m_date, m_hour, m_minute, m_second);
}

DateTime DateTime::currentDateTime()
{
    DateTime dateTime;
    time_t nowTime = ::time(NULL);
    struct tm* tmTime = gmtime(&nowTime);
    dateTime.setSecond(tmTime->tm_sec);
    dateTime.setMinute(tmTime->tm_min);
    dateTime.setHour(tmTime->tm_hour);
    dateTime.setDate(tmTime->tm_mday);
    dateTime.setMonth(tmTime->tm_mon+1);
    dateTime.setYear(tmTime->tm_year+1900);
    return dateTime;
}

int DateTime::setCurrentDateTime(DateTime& dateTime)
{
    struct tm currentTime;
    currentTime.tm_year = dateTime.year()-1900;
    currentTime.tm_mon = dateTime.month()-1;
    currentTime.tm_mday = dateTime.date();
    currentTime.tm_hour = dateTime.hour(); 
    currentTime.tm_min = dateTime.minute();
    currentTime.tm_sec = dateTime.second();
    currentTime.tm_isdst = 0;
    time_t seconds = mktime(&currentTime);
    if (seconds<0) 
    {
        TRACE_ERR("DateTime::setCurrentDateTime,make time error.\n");
        return -1;
    }
    struct timeval tv;
    struct timezone tz;
    if(gettimeofday(&tv,&tz)<0)
    {
        TRACE_ERR("DateTime::setCurrentDateTime,get time error.\n");
        return -1;
    }
    tv.tv_sec = seconds;
    tv.tv_usec = 0;
    if(settimeofday(&tv,&tz)<0)
    {
        TRACE_ERR("DateTime::setCurrentDateTime,set time error.\n");
        return -1;
    }
    return 0;
}

DateTime DateTime::getRTCDateTime()
{
    DateTime rtcTime;
#if USE_RTC
	int rtcFd=::open(RTC_DEV_PATH,O_RDWR);
    if (rtcFd<0)
    {
        TRACE_ERR("DateTime::getRTCDateTime,open rtc error:%s.\n",ERROR_STRING);
        return currentDateTime();
    }
    struct rtc_time rtc_tm;
    int ret=::ioctl(rtcFd, RTC_RD_TIME, &rtc_tm);
    if (ret<0)
    {
        TRACE_ERR("DateTime::getRTCDateTime,read rtc error:%s.\n",ERROR_STRING);
        close(rtcFd);
        return currentDateTime();    
    }
    rtcTime.setDate(rtc_tm.tm_mday);
    rtcTime.setMonth(rtc_tm.tm_mon+1); 
    rtcTime.setYear(rtc_tm.tm_year+1900);
    rtcTime.setHour(rtc_tm.tm_hour);
    rtcTime.setMinute(rtc_tm.tm_min); 
    rtcTime.setSecond(rtc_tm.tm_sec);
    close(rtcFd);
    return rtcTime;
#else
    return currentDateTime();
#endif
}

int DateTime::setRTCDateTime(DateTime& dateTime)
{
#if USE_RTC
    int rtcFd=::open(RTC_DEV_PATH,O_RDWR);
    if (rtcFd<0)
    {
        TRACE_ERR("DateTime::setRTCDateTime,open rtc error:%s.\n",ERROR_STRING);
        return -1;
    }
    struct rtc_time rtc_tm;
    rtc_tm.tm_mday = dateTime.date();
    rtc_tm.tm_mon = dateTime.month()-1;
    rtc_tm.tm_year = dateTime.year()-1900;
    rtc_tm.tm_hour = dateTime.hour(); 
    rtc_tm.tm_min = dateTime.minute();
    rtc_tm.tm_sec = dateTime.second();
    int ret=::ioctl(rtcFd, RTC_SET_TIME, &rtc_tm);
    if (ret<0)
    {
        TRACE_ERR("DateTime::setRTCDateTime,write rtc error:%s.\n",ERROR_STRING);
        close(rtcFd);
        return -1;    
    }
    close(rtcFd);
	return -1;
#else
    return setCurrentDateTime(dateTime);
#endif
}
}
